package transclusion;

import java.util.ArrayList;

import processing.core.*;
import transclusion.items.*;
import transclusion.util.*;
import wordcram.*;
import controlP5.*;

@SuppressWarnings("serial")
public class UniverseVisualization extends PApplet {
	PGraphics buffer;
	DAG dag;
	Path path;
	CategoryItem rootItem;
	SourceItem rolloverItem;
	SourceItem sourceArticleItem;
	CategoryItem taggedItem;

	BoundsIntegrator zoomBounds;
	CategoryItem zoomItem;
	SourceItem nextItem;

	RankedLongArray modTimes = new RankedLongArray();
	PFont font;

	WordCram wc;
	boolean transclusionIsShown = false;
	
	ControlP5 controlP5;
	DropdownList p1, p2;
	ArrayList<Category.IndexedItem> catDescendants;
	ArrayList<Article.IndexedItem> artDescendants;	

	public void setup() {		
		size(640, 360);
		buffer = createGraphics(width, height, JAVA2D);

		zoomBounds = new BoundsIntegrator(0, 0, width, height);

		cursor(CROSS);
		rectMode(CORNERS);
		smooth();
		noStroke();

		font = createFont("SansSerif", 13);
		
		dag = new DAG(this, "c0");		
		setRoot();
		
		catDescendants = new ArrayList<Category.IndexedItem>();
		artDescendants = new ArrayList<Article.IndexedItem>();		
		path = new Path(this);
		
		controlP5 = new ControlP5(this);
		p1 = controlP5.addDropdownList("Categories",20,30,100,120);		
		customize(p1);
		p2 = controlP5.addDropdownList("Articles",140,30,100,120);
		customize(p2);
		
		p1.hide();
		p2.hide();
	}
	
	void customize(DropdownList ddl) {
		ddl.setBackgroundColor(color(190));
		ddl.setItemHeight(20);
		ddl.setBarHeight(15);		
		ddl.captionLabel().style().marginTop = 3;
		ddl.captionLabel().style().marginLeft = 3;
		ddl.valueLabel().style().marginTop = 3;
		ddl.setColorBackground(color(60));
		ddl.setColorActive(color(255,128));
	}

	void setRoot() {
		CategoryItem tm = new CategoryItem(dag, null, dag.rootCategory(), 0, 0);
		tm.setBounds(0, 0, width, height);
		tm.contentsVisible = true;

		rootItem = tm;
		rootItem.zoomIn();
		rootItem.updateColors();
	}

	public void draw() {
		background(0);
		textFont(font);
		if (!this.transclusionIsShown) {
			frameRate(30);
			zoomBounds.update();
			rolloverItem = null;
			taggedItem = null;
			if (rootItem != null)
				rootItem.draw();
			if (rolloverItem != null)
				rolloverItem.drawTitle();
			if (taggedItem != null)
				taggedItem.drawTag();			
			
			if( path.active() ) {
				if( !path.busy() ) {
					SourceItem item = path.nextItem();
					//println("next item is: " + item.name);
					
					if (item instanceof CategoryItem) {
						if( ! ((CategoryItem) item).contentsVisible ) {
							((CategoryItem) item).open();
						}
						else {
							//println("zooming");
							((CategoryItem) item).zoomIn();
							if( item.name.equals(path.path.get(path.path.size() -1).name() ) )
								path.invalidate();
						}
					}
					else {
						item.open();
						path.invalidate();
					}
				}
			}
			
		} else if (this.transclusionIsShown) {		
			buffer.beginDraw();
			colorMode(PApplet.HSB, 360, 100, 100);			
			//buffer.background(255, 0, 0);
			int color = color(0, 80, sourceArticleItem.brightness);
			buffer.background(color);
			colorMode(PApplet.RGB, 255);
			wc = new WordCram(this).withColorer(new WordColorer() {
				public int colorFor(Word w) {
					if ( (Boolean) w.getProperty("isSource") == true) {
						return color(255, 255, 255);
					} else {
						return color(0, 255, 255);
					}
				}
			});
			wc.fromWords(((Article) sourceArticleItem.item).wordsArray())
			  .withCustomCanvas(buffer).drawAll();
			buffer.endDraw();
			image(buffer, 0, 0);
			noLoop();			
		}
	}
	
	public void mouseClicked() {
		if( p1.isVisible() || p2.isVisible() || path.active() )
			return;
		if (transclusionIsShown) {
			if(wc != null) {
				Word wordAtMouseClick = wc.getWordAt(mouseX, mouseY);
				if (wordAtMouseClick != null) {
					String url = "http://en.wikipedia.org/wiki/";
					url = url.concat( wordAtMouseClick.word );
					println("opening " + url);
					link(url);
				}
				else {
					loop();				
					transclusionIsShown = false;
					wc = null;
					cursor(CROSS);
				}
			}
		} else if (zoomItem != null /** && !path.active() */) {
			zoomItem.mouseClicked();
		}
	}		
	
	public void mouseMoved() {
		if( p1.isVisible() || p2.isVisible() )
			return;
		if (!this.transclusionIsShown)
			return;
		if(wc != null ) {
			Word wordAtMouseClick = wc.getWordAt(mouseX, mouseY);
			if (wordAtMouseClick != null)				
				cursor(HAND);
			else
				cursor(ARROW);
		}
	}
	
	public void keyPressed() {
		if ((key == 's' || key == 'S') && !transclusionIsShown && !path.active() && !p1.isVisible() && !p2.isVisible()) {
			dag = new DAG(this, "c0");
			setRoot();
		}
		else if ((key == 't' || key == 'T') && !transclusionIsShown && !path.active() && !p1.isVisible() && !p2.isVisible()) {
			dag = new DAG(this, "c0", false);
			setRoot();
		}
		else if(( key == 'c' || key == 'C')  && !transclusionIsShown && !path.active()) {
			p2.hide();
			if( p1.isVisible() ) {
				p1.clear();	
				p1.hide();				
			} else {
				p1.clear();	
				p1.captionLabel().set("Categories");
				
				((Category)zoomItem.item).articleDescendants(catDescendants, artDescendants);				
				for( int i = 0; i < catDescendants.size(); i++ )
					p1.addItem( ( i + 1 ) + ". " + catDescendants.get(i).item.name(), i );
				/**
				for( int i = 0; i < artDescendants.size(); i++ )
					p2.addItem( ( i + 1 ) + ". " + artDescendants.get(i).item.name(), i );
				*/
				
				p1.show();
			}
		}
		else if(( key == 'a' || key == 'A')  && !transclusionIsShown && !path.active()) {
			p1.hide();
			if( p2.isVisible() ) {
				p2.clear();			
				p2.hide();				
			} else {
				p2.clear();				
				p2.captionLabel().set("Articles");
				
				((Category)zoomItem.item).articleDescendants(catDescendants, artDescendants);
				/**
				for( int i = 0; i < catDescendants.size(); i++ )
					p1.addItem( ( i + 1 ) + ". " + catDescendants.get(i).item.name(), i );
				*/
				for( int i = 0; i < artDescendants.size(); i++ )
					p2.addItem( ( i + 1 ) + ". " + artDescendants.get(i).item.name(), i );
				
				p2.show();
			}
		}
		else if(key == ' ' && !transclusionIsShown && !path.active()) {
			if( p1.isVisible() || p2.isVisible() ) {
				p1.clear();
				p2.clear();
				p1.hide();
				p2.hide();
			} else {
				p1.clear();
				p2.clear();
				
				p1.captionLabel().set("Categories");
				p2.captionLabel().set("Articles");
				
				((Category)zoomItem.item).articleDescendants(catDescendants, artDescendants);				
				for( int i = 0; i < catDescendants.size(); i++ )
					p1.addItem( ( i + 1 ) + ". " + catDescendants.get(i).item.name(), i );
				for( int i = 0; i < artDescendants.size(); i++ )
					p2.addItem( ( i + 1 ) + ". " + artDescendants.get(i).item.name(), i );
				
				p1.show();				
				p2.show();
			}
		}		
	}
	
	public void controlEvent(ControlEvent event) {
		boolean fromCategories = false;
		boolean fromArticles = false;
		int id = -1;
		if (event.isGroup()) {
			id = (int)event.group().value();
			if (event.group().name().equals("Categories"))
				fromCategories = true;				
			else if (event.group().name().equals("Articles"))
				fromArticles = true;
		} else if(event.isController()) {
			id = (int)event.controller().value();
			if (event.controller().name().equals("Categories"))
				fromCategories = true;
			else if (event.controller().name().equals("Articles"))
				fromArticles = true;
		}
		if(id == -1)
			return;
		
		if(fromCategories)
			path.setPathFromIndexedCategory(id);
		else if (fromArticles)
			path.setPathFromIndexedArticle(id);
		
		p1.hide();				
		p2.hide();
	}

	public static void main(String args[]) {
		PApplet.main(new String[] { "--present", "transclusion.UniverseVisualization" });
	}
}
